////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
//  File name:   brush.h
//  Version:     v1.00
//  Created:     8/7/2002 by Timur.
//  Compilers:   Visual Studio.NET
//  Description: 
// -------------------------------------------------------------------------
//  History: Based on Andrey's Indoor editor.
//	26/02/2010 Refactored by Jaesik.
////////////////////////////////////////////////////////////////////////////

#ifndef __BRUSH_H__
#define __BRUSH_H__

#if _MSC_VER > 1000
#pragma once
#endif

#include "Geometry\EdGeometry.h"
#include "BrushCommon.h"

enum BrushFlags 
{
	BRF_MESH_VALID = 0x40,
	BRF_MODIFIED = 0x80,
	BRF_SUB_OBJ_SEL = 0x100,
	BRF_SELECTED    = 0x200,
};

struct SBrushSubSelection
{
	std::vector<Vec3*> points;
	bool AddPoint( Vec3 *pnt );
	void Clear();
};

struct SBrushFace;
struct SBrushVertex;
struct SBrush;
struct SBrushPlane;
struct SBrushTriangle;
struct SBrushFace;

class CSolidBrushObject;

struct SBrush : public CEdGeometry
{

public:

	virtual EEdGeometryType GetType() const { return GEOM_TYPE_BRUSH; };
	virtual void Serialize( CObjectArchive &ar )	{};
	virtual void GetBounds( AABB &box )						{	box = m_bounds;		}	
	virtual CEdGeometry* Clone();
	virtual bool IsModified() const								{	return m_nFlags & BRF_MODIFIED;	}
	virtual IIndexedMesh* GetIndexedMesh();
	virtual void SetModified( bool bModified=true );
	virtual void Display( DisplayContext &dc );
	virtual bool HitTest( HitContext &hit );
	virtual void ModifySelection( SSubObjSelectionModifyContext &modCtx, bool isUndo=true );
	virtual void AcceptModifySelection() {}	
	virtual bool StartSubObjSelection( const Matrix34 &nodeWorldTM,int elemType,int nFlags );
	virtual void EndSubObjSelection();


public:

	SBrush();	
	virtual ~SBrush();

	SBrush& operator = (const SBrush& b);

	void Create( const Vec3 &mins, const Vec3& maxs, int numSides, SBrushCommon::ESolidBrushCreateType shapetype=SBrushCommon::eBrushCreateType_Box );	
	void CreateSimpleMesh(const std::vector<Vec3>& vtxs, const float height);

	bool BuildBrush(	bool bComputeAdjacentTriangle, 
										bool bComputeCoplanarTriangle, 
										bool bComputeTexCoord = true, 
										bool bComputeBoundBox = true );
	void RecalcTexCoords();
	bool Clone( _smart_ptr<SBrush>& brush );
	void Move( Vec3& delta );
	bool SplitByPlane( const SBrushPlane& plane, SBrush* &front, SBrush* &back, bool bJustForDisplay=false ) const;
	void FitTexture(int nX, int nY);
	void Transform( const Matrix34 &tm, bool bUndo = false );
	void SnapToGrid( bool bOnlySelected=false );
	void SelectSide( Vec3 Origin, Vec3 Dir,bool shear,SBrushSubSelection &subSelection );	
	int  GetMemorySize();
	void ClearFaces();	
	void Serialize( XmlNodeRef &xmlNode, bool bLoading, bool bUndo );
	void RecordUndo( const char *sUndoDescription=0 );
	void GenerateIndexMesh(IIndexedMesh *pMesh);	
	void OptimizeMesh(IIndexedMesh* pMesh);	
	bool ClearSelection();
	void SetSelected( bool bSelected )			{ m_nFlags = (bSelected) ? (m_nFlags|BRF_SELECTED) : (m_nFlags&(~BRF_SELECTED)); }
	void SetMatrix( const Matrix34 &tm, bool Transfer2Owner = false );
	const Matrix34& GetMatrix() const				{	return m_matrix;	}
	void Invalidate()												{	m_nFlags &= ~BRF_MESH_VALID;	}
	bool UpdateMesh();
	bool MergeBrush( const SBrush* brush, Vec3* newPivot );	
	void SetVertexList( const std::vector<Vec3>& poslist );
	void AddFace( const std::vector<SBrushCommon::VertexFaceIndexType>& pointindexlist, 
								const SBrushCommon::STexInfo* texinfo = NULL, 
								const short* matID = NULL );	
	void AdjustPivot();

	bool GetIStatObj( IStatObj*& pOutStatObj );	

	AABB GetWorldBound() const;
	const AABB& GetBoundBox() const			{	return m_bounds;					}
	int GetFlags() const								{	return m_nFlags;					}
	int GetZeroTolerance() const				{	return m_isZeroTolerance;	}	

	void PivotToCenter( Vec3* newPivot );

	void Selection_SnapToGrid();
	void Selection_DeleteVertex();
	void Selection_SplitFace();
	void Selection_FlipFace();
	void Selection_DeleteFace();
	void Selection_SetMatId( int nMatId );
	void Selection_SelectMatId( int nMatId );

	void SetOwner( CSolidBrushObject* owner )		{	m_Owner = owner;}
	CSolidBrushObject* GetOwner()								{	return m_Owner;	}


public:

	static SBrush* CreateBrush( CSolidBrushObject* obj );


private:

	SBrush( const SBrush& b );

	static bool& GetEditModeFlag()
	{
		static bool boEditModeFlag(false);
		return boEditModeFlag;
	}

	static const float VER_EPS;
	static const float VER_TIGHTEPS;
	static const float BASIS_COPLANARPLANE;


public:

	size_t GetNumberOfFaces() const;
	size_t GetNumberOfTriangles() const;
	const Vec3& GetFaceNormal( int idx ) const;
	size_t GetNumberOfFaceEdges( SBrushFace* face ) const;
	float GetFaceDistance( int idx ) const;
	size_t GetNumberOfFacePoints( int idx ) const;
	const Vec3& GetFacePointPos( int idx, int vidx ) const;
	bool IsValidFace( int idx ) const;
	bool IsPointSelected( int idx, int vidx ) const;
	bool IsFaceSelected( int idx ) const;
	void SetFaceSelected( int idx, bool bSelected ) const;
	size_t GetNumberOfFaceEdges( int idx ) const;
	int GetFaceSelectedEdge( int idx ) const;
	const Vec3& GetFaceCenter( int idx ) const;
	void CalculateFaceCenter( int idx ) const;
	void CalculateFaceTextureBasis( int idx, Vec3& tu, Vec3& tv) const;
	void CalculateFaceBound( int idx, Vec3& mins, Vec3& maxs ) const;
	const SBrushCommon::STexInfo& GetFaceTexInfo( int idx ) const;
	void SetFaceTexInfo( int idx, const SBrushCommon::STexInfo& ti )	const;
	int	 GetFaceMaterialID( int idx ) const;
	void SetFaceMaterialID( int idx, int matID )	const;
	void FitFaceTexture( int idx, float fTileU, float fTileV ) const;
	void MapEdgeIndexToPolyIndices( int idx, const int& nEdgeIndex, int &rnFirstPolyVertex, int& rnSecondPolyVertex ) const;
	size_t GetNumberOfVertices() const;
	const Vec3& GetVertexPos( int idx ) const;
	void SetVertexPos( int idx, const Vec3& pos );
	SBrushCommon::VertexFaceIndexType GetFaceVertexIndex( int idx, int pidx ) const;
	const SBrushPlane& GetFacePlane( int idx ) const;
	const AABB& GetFaceBBox( int idx ) const;	

	static bool FindTrianglePointsInPolygonForFace( 
			const std::vector<Vec3>& poslist, 
			SBrushCommon::VertexFaceIndexType& triIndex0, 
			SBrushCommon::VertexFaceIndexType& triIndex1, 
			SBrushCommon::VertexFaceIndexType& triIndex2 );

	SBrushTriangle* AddTriangle();
	SBrushTriangle* AddTriangle(SBrushCommon::VertexFaceIndexType vertexindex0, SBrushCommon::VertexFaceIndexType vertexindex1, SBrushCommon::VertexFaceIndexType vertexindex2, SBrushCommon::VertexFaceIndexType faceindex=SBrushCommon::VertexFaceMax);
	SBrushTriangle* AddTriangle(const Vec3& v0, const Vec3& v1, const Vec3& v2, SBrushCommon::VertexFaceIndexType i0, SBrushCommon::VertexFaceIndexType i1, SBrushCommon::VertexFaceIndexType i2, SBrushCommon::VertexFaceIndexType fidx=SBrushCommon::VertexFaceMax);

	void AddTriangle( SBrushFace* face, const SBrushTriangle& triangle );
	void RemoveTriangle(SBrushFace* face,  int index );

	SBrushVertex* AddEmptyVertex();
	SBrushVertex* AddVertex( const Vec3& pos );


private:

	bool IsValid() const;

	bool IsFaceListEmpty() const	{	return	m_BrushFaces.empty();	}
	SBrushFace* RayHit( Vec3 rayOrigin,Vec3 rayDir, float *dist ) const;

	void ComputeAdjacentTriangle();
	void ComputeCoplanarTriangle();
	void ComputeFacePlanes();
	bool ComputeBoundBox();
	void ComputeTexCoord();
	void ComputeAdjacentCoplanar( int current, SBrushFace* face, SBrushCommon::VertexFaceIndexType polyidx );		

	bool DeleteSelectedFace();

	Vec3 CalcNormal( const Vec3& v0, const Vec3& v1, const Vec3& v2 ) const;

	bool SelectFace( HitContext& hit, const Vec3& osCameraDir, const Matrix34& worldTM );
	bool SelectEdge( HitContext& hit, const Vec3& osCameraDir, const Matrix34& worldTM );
	bool SelectVertex( HitContext& hit, const Vec3& osCameraPos, const Matrix34& worldTM );	

	void CountFrontBackVertex(	const SBrushPlane& plane, 
															std::vector<char>& vsign, 
															SBrushCommon::VertexFaceIndexType& front, SBrushCommon::VertexFaceIndexType& back, SBrushCommon::VertexFaceIndexType& zero ) const;

	void FindMeetVertex(	const SBrushPlane& plane, 
												const std::vector<char>& vsign, 
												const SBrushFace* face,
												const bool& bJustForDisplay,
												std::vector<Vec3>& mvl,
												int& touchcount,
												SBrushCommon::VertexFaceIndexType* touchlocation,
												Vec3* contactpt ) const;

	bool SplitFace( const std::vector<char>& vsign, 
									const SBrushFace* face,
									const SBrushCommon::VertexFaceIndexType* touchlocation,
									const Vec3* contactpt,
									const bool& bJustForDisplay,
									SBrush* frontbrush,
									SBrush* backbrush ) const;

	void PutFaceIntoOneSide(	const std::vector<char>& vsign, 
														const SBrushFace* face,
														const bool& bJustForDisplay,
														SBrush* frontbrush,
														SBrush* backbrush,
														std::vector<SBrushCommon::VertexFaceIndexType>& frontvidxmap,
														std::vector<SBrushCommon::VertexFaceIndexType>& backvidxmap ) const;

	void SortForMakingConvexHullFace(	const std::vector<Vec3>& mvl, 
																		std::vector<SBrushCommon::VertexFaceIndexType>& sml ) const;

	bool CheckIfFaceIsConvexHull(	const std::vector<Vec3>& mvl, 
																const std::vector<SBrushCommon::VertexFaceIndexType>& sml ) const;

	void MakeConvexHullFace(	const SBrushPlane& plane,
														const std::vector<Vec3>& mvl, 
														const std::vector<SBrushCommon::VertexFaceIndexType>& sml,
														SBrush* frontbrush,
														SBrush* backbrush ) const;

	bool IsConvexHull() const;
	bool UpdateFromFace( SBrushCommon::VertexFaceIndexType fidx );
	
	void ExcludeInvalidData();


private:

	void RemoveDuplicatedVerticesInFace();
	void MakeInvalidTriangleWithVerySmallAreaNullPointer();
	void DeleteInvalidTrianglesInFace();
	void MakeInvalidTriangleWithInvalidFaceNullPointer();

	template<typename T>
	void MakeNewListAndMapperForCreatingNewValidList( std::vector<T>& inputList, std::vector<T>& outList, std::map<int,int>& outMapper );

	void MapOldFaceIndexToNewFaceIndexInTriangleList( std::vector<SBrushTriangle*>& triangleList, std::map<int,int>& faceMapper );
	void MapOldTriangleIndexToNewTriangleIndexInFaceList( std::vector<SBrushFace*>& faceList, std::map<int,int>& triangleMapper );

private:

	struct SBackupBrushInfo
	{
		SBackupBrushInfo(){}
		~SBackupBrushInfo();
		std::vector<SBrushVertex*>		m_Vertices;
		std::vector<SBrushTriangle*>	m_Triangles;
		std::vector<SBrushFace*>			m_Faces;
	};

	void BackupBrush( SBackupBrushInfo& BackupInfo );
	void RestoreBrush( SBackupBrushInfo& BackupInfo );

	friend class CBrushSerialize;


private:

	std::vector<SBrushVertex*>		m_BrushVertices;
	std::vector<SBrushTriangle*>	m_BrushTriangles;
	std::vector<SBrushFace*>			m_BrushFaces;


private:

	AABB	m_bounds;
	int		m_nFlags;
	int		m_isZeroTolerance;

	CSolidBrushObject* m_Owner;

	Matrix34		m_matrix;
	IStatObj *	m_pStatObj;
	int					m_selectionType;

};

#endif __BRUSH_H__
